#pragma once
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <string.h>
#include <thread>
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
using namespace std;

const string defaultip = "0.0.0.0";
const int backlog = 10;
extern Log log;

enum {
    SOCKET_ERR,
    IP_ERR,
    BIND_ERR,
    LISTEN_ERR,
};

class TcpServer
{
private:
    int _listenFd = -1;
    uint16_t _port;
    string _ip;
public:
    TcpServer();
    TcpServer(const uint16_t& port, const string& ip);
    ~TcpServer();
    void initServer();
    void run();
private:
    void service(const int& socketFd, const string& ip, const uint16_t& port);
    void serviceHandler(const int& socketFd, const string& ip, const uint16_t& port);
};

TcpServer::TcpServer()
{}

inline TcpServer::TcpServer(const uint16_t& port, const string& ip=defaultip) : _port(port), _ip(ip)
{}

TcpServer::~TcpServer()
{}

void TcpServer::initServer()
{
    // 创建套接字
    _listenFd = socket(AF_INET, SOCK_STREAM, 0);

    if(_listenFd == -1) {
        log(FATAL, "创建套接字失败, %s\n", strerror(errno));
        exit(SOCKET_ERR);
    }

    log(INFO, "创建套接字成功, fd: %d\n", _listenFd);
    sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(_port);

    if (inet_aton(_ip.c_str(), &(local.sin_addr)) == 0) {
        log(FATAL, "inet_aton, IP不合法, %s\n", strerror(errno));
        exit(IP_ERR);
    };

    // 绑定
    if(bind(_listenFd, (sockaddr*)&local, sizeof(local)) == -1) {
        log(FATAL, "绑定失败, %s\n", strerror(errno));
        exit(BIND_ERR);
    }

    log(INFO, "绑定成功, fd: %d\n", _listenFd);

    // 监听
    if(listen(_listenFd, backlog) == -1) {
        log(FATAL, "监听失败, %s\n", strerror(errno));
        exit(LISTEN_ERR);
    }

    log(INFO, "监听成功, fd: %d\n", _listenFd);
}

void TcpServer::run()
{
    // signal(SIGCHLD, SIG_IGN);
    ThreadPool<Task>::GetInstance()->Start();
    printf("tcp server is running...\n");
    for(;;) {
        sockaddr_in client;
        socklen_t len = sizeof(client);
        // 接受请求
        int socketFd = accept(_listenFd, (sockaddr*)&client, &len);

        if(socketFd == -1) {
            log(WARNING, "接受请求失败, %s\n", strerror(errno));
            sleep(1);
            continue;
        }

        // 获得客户端的ip和端口号
        uint16_t port = ntohs(client.sin_port);
        char ip[32];       // INET_ADDRSTRLEN 是16
        memset(ip, 0, sizeof ip);

        if(inet_ntop(AF_INET, &(client.sin_addr), ip, 32) == nullptr) {
            log(FATAL, "inet_ntop, IP不合法, %s\n", strerror(errno));
            exit(IP_ERR); 
        }

        log(INFO, "获得了一个新链接, 新的socket fd: %d, 客户端ip: %s, 客户端端口号: %d\n", socketFd, ip, port);

        /* version 1 */
        // service(socketFd, ip, port);
        // close(socketFd);

        /* version 2 多进程 */
        // pid_t pid = fork();
        // if(pid < 0) {
        //     log(WARNING, "fork()失败, 原因:%s\n", strerror(errno));
        //     sleep(1);
        //     continue;
        // } else if(pid == 0) {
        //     close(_listenFd);           // 关闭_listenFd的目的是为了防止子进程修改改文件描述符下的内容
        //     if(fork() > 0)  exit(0);    // 关闭子进程，让孙子进程执行，防止父进程在waitpid的时候阻塞
        //     service(socketFd, ip, port);
        //     close(socketFd);            // 不要忘了关闭socketFd
        //     // exit(0);                    // 已经关闭了文件描述符socketFd了，为什么还必须要exit()呢？
        // }  else {
        //     // 父进程
        //     close(socketFd);            // 父进程关闭socketFd的意义是为了防止文件描述符越用越少，关闭了之后能保证每一个新的进程用的都是fd都是4
        //     waitpid(pid, nullptr, 0); 
        // }

        /* version 3 多线程 */
        // 这里为什么必须用=, 用&就会失败?
        // thread([=]      
        // {
        //     service(socketFd, ip, port);
        //     close(socketFd);
        //     return 0;
        // }).detach();

        /* version 4 线程池 */
        Task t(socketFd, ip, port);
        ThreadPool<Task>::GetInstance()->Push(t);
    }
}

void TcpServer::service(const int &socketFd, const string &ip, const uint16_t &port)
{
    char buf[1024];
    while(true) {
        // 从socketFd中读取数据
        ssize_t n = read(socketFd, buf, sizeof (buf) - 1);
        if(n > 0) {
            buf[n] = 0;          // 确保字符串以空字符结尾
            // 写回客户端
            string echoString = "server get a message$ ";
            echoString += buf;
            printf("client say$ %s\n", buf);
            write(socketFd, echoString.c_str(), echoString.size());
        } else if(n == 0) {
            log(INFO, "客户端 %s:%d 退出, 关闭fd: %d\n", ip.c_str(), port, socketFd);
            break;
        } else {
            log(WARNING, "读取客户端 %s:%d 信息失败, 关闭fd: %d\n", ip.c_str(), port, socketFd);
            break;
        }
    }
}
